/****************************************************************
 *
 * Project: IMU
 * 
 ****************************************************************/

/****************************************************************
 *
 *	Module		: "header file" mySpi.h
 *  Tool		: KeilC ARM 4.22a
 *	Chip		: STM32F407VG
 * 	History		: 30/06/2012
 *	Description	: Day 12
				  + My library to Read/Write data over I2C bus
				  + Reference to header file "myI2C.h"
 *				
 *	Author		: Dang Anh Tung			
 *	Description	: www.payitforward.edu.vn
 *				
 *
 ****************************************************************/

 
 /****************************************************************
 * 							IMPORT
 ****************************************************************/

#include "myI2C.h"


 /****************************************************************
 * 							GLOBAL
 ****************************************************************/

DMA_InitTypeDef DmaI2CTxStruct;
uint8_t DmaI2CTxBuffer[20];

DMA_InitTypeDef DmaI2CRxStruct;
uint8_t DmaI2CRxBuffer[20]; 

BOOL I2C_TX_CompleteFlag = FALSE;
BOOL I2C_RX_CompleteFlag = FALSE;

/*****************************************************************
* 							DEFINE
******************************************************************/


 /****************************************************************
 * 						   FUNCTIONS
 ****************************************************************/

/****************************************************************
** Function name:	    myI2CInit
**
** Descriptions:		Init I2C peripheral on STM
**
** parameters:		    None
** Returned value:		None
** 
*****************************************************************/
void myI2CInit(void)
{
	GPIO_InitTypeDef I2C_GPIO_InitStructure;
    I2C_InitTypeDef  I2C_InitStructure;

    /*
	*	Init I2C2
	*	PB10 I2C2_SCL
	*	PB11 I2C2_SDA
	*/

	//	Enable clock I2C2 peripheral
	RCC_APB1PeriphClockCmd(I2C_CLOCK, ENABLE);
	//	Enable clock to pin SCL, SDA
	RCC_AHB1PeriphClockCmd(I2C_SCL_CLOCK, ENABLE);
    RCC_AHB1PeriphClockCmd(I2C_SDA_CLOCK, ENABLE);  
	//  Init GPIO
	I2C_GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
	I2C_GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	I2C_GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
	I2C_GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;		//note 
	//	
	I2C_GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN;  
	GPIO_Init(I2C_SCL_PORT, &I2C_GPIO_InitStructure);

	I2C_GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN;  
	GPIO_Init(I2C_SDA_PORT, &I2C_GPIO_InitStructure);

    //	Configure alternate function to pins 
	GPIO_PinAFConfig(I2C_SCL_PORT, I2C_SCL_PIN_SOURCE, I2C_SCL_AF);    
	GPIO_PinAFConfig(I2C_SDA_PORT, I2C_SDA_PIN_SOURCE, I2C_SDA_AF);  

    //  I2C configuration 
	I2C_DeInit(I2C_SOURCE);
	I2C_InitStructure.I2C_ClockSpeed 			= I2C_SPEED;	
	I2C_InitStructure.I2C_Mode 					= I2C_Mode_I2C;					
	I2C_InitStructure.I2C_DutyCycle 			= I2C_DutyCycle_2;							
	I2C_InitStructure.I2C_OwnAddress1 			= 0;
	I2C_InitStructure.I2C_Ack 					= I2C_Ack_Enable;						
	I2C_InitStructure.I2C_AcknowledgedAddress 	= I2C_AcknowledgedAddress_7bit;
	I2C_Init(I2C_SOURCE, &I2C_InitStructure);

	//	Enable I2C2
    I2C_Cmd(I2C_SOURCE, ENABLE);


}

/****************************************************************
** Function name:	    myI2CInit
**
** Descriptions:		Init I2C peripheral on STM
**
** parameters:		    None
** Returned value:		None
** 
*****************************************************************/
void myDmaI2CInit(void)
{ 

	//	Enable clock to DMA1
	RCC_AHB1PeriphClockCmd(DMA_CLOCK, ENABLE);

	//default reset state
    DMA_DeInit(DMA_I2C_TX_STREAM);			
	DmaI2CTxStruct.DMA_Channel 				    = DMA_I2C_TX_CHANNEL;
	DmaI2CTxStruct.DMA_DIR 					    = DMA_DIR_MemoryToPeripheral;
	DmaI2CTxStruct.DMA_Memory0BaseAddr 		    = (uint32_t)DmaI2CTxBuffer; 
    DmaI2CTxStruct.DMA_PeripheralBaseAddr 		= DMA_I2C_TX_PERIPH;  		 
	DmaI2CTxStruct.DMA_PeripheralDataSize 		= DMA_PeripheralDataSize_Byte;
	DmaI2CTxStruct.DMA_MemoryDataSize 			= DMA_MemoryDataSize_Byte;
	DmaI2CTxStruct.DMA_Priority 				= DMA_I2C_TX_PRIORITY;
	DmaI2CTxStruct.DMA_FIFOMode 				= DMA_FIFOMode_Disable;				//direct mode
	DmaI2CTxStruct.DMA_FIFOThreshold 			= DMA_FIFOThreshold_Full;
	DmaI2CTxStruct.DMA_MemoryInc 				= DMA_MemoryInc_Enable;
	DmaI2CTxStruct.DMA_PeripheralInc 			= DMA_PeripheralInc_Disable;
	DmaI2CTxStruct.DMA_Mode 					= DMA_Mode_Normal;
	DmaI2CTxStruct.DMA_BufferSize 				= (uint32_t)7 ;						//JUST init value
	DmaI2CTxStruct.DMA_MemoryBurst 			    = DMA_MemoryBurst_Single;
	DmaI2CTxStruct.DMA_PeripheralBurst 		    = DMA_PeripheralBurst_Single;
	//	disable DMA Stream Transfer Complete interrupt
	DMA_ITConfig(DMA_I2C_TX_STREAM, DMA_IT_TC, ENABLE);
	DMA_Init(DMA_I2C_TX_STREAM, &DmaI2CTxStruct);	

	
	//default reset state
	DMA_DeInit(DMA_I2C_RX_STREAM);				
	DmaI2CRxStruct.DMA_Channel 				    = DMA_I2C_RX_CHANNEL;
	DmaI2CRxStruct.DMA_DIR 					    = DMA_DIR_PeripheralToMemory;
	DmaI2CRxStruct.DMA_Memory0BaseAddr 		    = (uint32_t)DmaI2CRxBuffer; 
    DmaI2CRxStruct.DMA_PeripheralBaseAddr 		= DMA_I2C_RX_PERIPH;  		 
	DmaI2CRxStruct.DMA_PeripheralDataSize 		= DMA_PeripheralDataSize_Byte;
	DmaI2CRxStruct.DMA_MemoryDataSize 			= DMA_MemoryDataSize_Byte;
	DmaI2CRxStruct.DMA_Priority 				= DMA_I2C_RX_PRIORITY;
	DmaI2CRxStruct.DMA_FIFOMode 				= DMA_FIFOMode_Disable;				//direct mode
	DmaI2CRxStruct.DMA_FIFOThreshold 			= DMA_FIFOThreshold_Full;
	DmaI2CRxStruct.DMA_MemoryInc 				= DMA_MemoryInc_Enable;
	DmaI2CRxStruct.DMA_PeripheralInc 			= DMA_PeripheralInc_Disable;
	DmaI2CRxStruct.DMA_Mode 					= DMA_Mode_Normal;
	DmaI2CRxStruct.DMA_BufferSize 				= (uint32_t)7 ;						//JUST init value
	DmaI2CRxStruct.DMA_MemoryBurst 			    = DMA_MemoryBurst_Single;
	DmaI2CRxStruct.DMA_PeripheralBurst 		    = DMA_PeripheralBurst_Single;
	//  Disable DMA Stream Transfer Complete interrupt
	DMA_ITConfig(DMA_I2C_RX_STREAM, DMA_IT_TC, ENABLE);
	DMA_Init(DMA_I2C_RX_STREAM, &DmaI2CRxStruct);	

}


void myDmaI2CInterruptInit(void)
{
	NVIC_InitTypeDef NVIC_InitStruct;

	
	/*
	*	I2C TX Interrupt
	*
	*/	

	NVIC_InitStruct.NVIC_IRQChannel = DMA_I2C_TX_STREAM_IRQ;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;			  //sub- receive interrupt highest = 0
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStruct);							// disable TC flag of trasmission 

 	/*
	*   I2C RX Interrupt
	*
	*/
		
	NVIC_InitStruct.NVIC_IRQChannel = DMA_I2C_RX_STREAM_IRQ;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;			  //sub- receive interrupt highest = 0
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStruct); 
       
}

/****************************************************************
** Function name:	    myDmaI2CConfig
**
** Descriptions:		Init I2C peripheral on STM
**
** parameters:		    None
** Returned value:		None
** 
*****************************************************************/
void myDmaI2CConfig(uint32_t pBuffer, uint32_t BufferSize, uint32_t Direction)
{ 
    /* Initialize the DMA with the new parameters */
    if (Direction == I2C_Direction_Transmitter)
    {
        /* Configure the DMA Tx Channel with the buffer address and the buffer size */
        DmaI2CTxStruct.DMA_Memory0BaseAddr = (uint32_t)pBuffer;
        DmaI2CTxStruct.DMA_BufferSize = (uint32_t)BufferSize;  
        DMA_Init(DMA_I2C_TX_STREAM, &DmaI2CTxStruct);  
    }
    else
    { 
        /* Configure the DMA Rx Channel with the buffer address and the buffer size */
        DmaI2CRxStruct.DMA_Memory0BaseAddr = (uint32_t)pBuffer;
        DmaI2CRxStruct.DMA_BufferSize = (uint32_t)BufferSize;      
        DMA_Init(DMA_I2C_RX_STREAM, &DmaI2CRxStruct);    
    }
}

/****************************************************************
** Function name:	    myI2CReadBytes
**
** Descriptions:		Read multi-bytes (numByte) continuously 
**						begin at start address in 
**						I2C slave by the NORMAL BUS(not DMA)
**
** parameters:		    None
** Returned value:		None
** 
*****************************************************************/	
BOOL myI2CReadBytes(I2C_TypeDef* I2Cx, uint8_t *readBuffer, uint8_t devAddr, uint8_t startAddr, uint32_t numByte)
{
	uint32_t time;

    if (numByte == 0)   return FALSE;

	/* While the bus is busy */
	time = I2C_TIMEOUT;
	while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY))
	{
		if((time--) == 0) return FALSE;
	}
	
	/* Send START condition */
	I2C_GenerateSTART(I2Cx, ENABLE);
	
	/* Test on EV5 and clear it (cleared by reading SR1 then writing to DR) */
	time = I2C_TIMEOUT;
	while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
	{
		if((time--) == 0) return FALSE;
	}
	
	/* Send Sensor address for write */
	I2C_Send7bitAddress(I2Cx, devAddr, I2C_Direction_Transmitter);
    	
	/* Test on EV6 and clear it */
	time = I2C_TIMEOUT;
	while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
	{
		if((time--) == 0) return FALSE;
	} 
    	
	/* Send the Register address to read from: Only one byte address */
	I2C_SendData(I2Cx, startAddr);  
	
	/* Test on EV8 and clear it */
	time = I2C_TIMEOUT;
	while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) 
	{
		if((time--) == 0) return FALSE;
	}
	
	/* Send START condition a second time to RESTART bus */
	I2C_GenerateSTART(I2Cx, ENABLE);
	
	/* Test on EV5 and clear it (cleared by reading SR1 then writing to DR) */
	time = I2C_TIMEOUT;
	while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
	{
		if((time--) == 0) return FALSE;
	} 
	
	/* Send Sensor address for read */
	I2C_Send7bitAddress(I2Cx, devAddr, I2C_Direction_Receiver);  

#ifndef _USE_I2C_DMA_
    /* SHOULD DISABLE INTERRUPT HERE, IF NOT IT MAY BE CORRUPT I2C TRANSACTION */
    __disable_irq();
    	
    /* Test on EV6 and clear it */
    time = I2C_TIMEOUT;
    while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
    {
        if ((time--) == 0) return FALSE;
    }

    while(numByte)
    {
        if(numByte == 1)
        {
            /* This configuration should be in the last second transfer byte */
            /* Disable Acknowledgement */
            I2C_AcknowledgeConfig(I2Cx, DISABLE);
            
            /* Send STOP Condition */
            I2C_GenerateSTOP(I2Cx, ENABLE);
        }

        /* Test on EV7 and clear it */
        time = I2C_TIMEOUT;
        while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED))
        {
            if ((time--) == 0) return FALSE;
        }

        /* Read the byte received from the Sensor */
        /* Point to the next location where the byte read will be saved */
        *readBuffer = I2C_ReceiveData(I2Cx);
        readBuffer++;
        
        /* Decrease the read bytes counter */
        numByte--;

    }

    /* Wait to make sure that STOP control bit has been cleared */
    time = I2C_TIMEOUT;
    while(I2Cx->CR1 & I2C_CR1_STOP)
    {
        if((time--) == 0) return FALSE;
    }  
    
    /* Re-Enable Acknowledgement to be ready for another reception */
    I2C_AcknowledgeConfig(I2Cx, ENABLE);  
    
    /* ENABLE ALL INTERRUPT */
    __enable_irq();     
    return TRUE; 

#else   /* #ifdef _USE_I2C_DMA_ */

    /*---------------------------------------------------------------
    *	If number of data to be read is 1, then DMA couldn't be used 
    *   One Byte Master Reception procedure (POLLING)
    ----------------------------------------------------------------*/
	if (numByte < 2)
	{
		/* Wait on ADDR flag to be set (ADDR is still not cleared at this level */
		time = I2C_TIMEOUT;
		while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_ADDR) == RESET)
		{
		    if((time--) == 0) return FALSE;
		}     
    	
		/* Disable Acknowledgement: no ACK after receiving data */
		I2C_AcknowledgeConfig(I2Cx, DISABLE);   
		
        /* Call User callback for critical section start (should typically disable interrupts) */
        // __disable_irq();

		/* Clear ADDR register by reading SR1 then SR2 register (SR1 has already been read) */
		(void)I2Cx->SR2;
		
		/* Send STOP Condition */
		I2C_GenerateSTOP(I2Cx, ENABLE);

         /* Call User callback for critical section end (should typically re-enable interrupts) */
        //__enable_irq();	

		/* Wait for the byte to be received */
		time = I2C_TIMEOUT;
		while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_RXNE) == RESET)
		{
			if((time--) == 0) return FALSE;
		}
		
		/* Read the byte received from the Sensor */
		*readBuffer = I2C_ReceiveData(I2Cx);
		
		/* Decrement the read bytes counter */
		numByte--;       
		
		/* Wait to make sure that STOP control bit has been cleared */
		time = I2C_TIMEOUT;
		while(I2Cx->CR1 & I2C_CR1_STOP)
		{
		  if((time--) == 0) return FALSE;
		}  
		
		/* Re-Enable Acknowledgement to be ready for another reception */
		I2C_AcknowledgeConfig(I2Cx, ENABLE);    
	}

    /*---------------------------------------------------------------
    *	More than one Byte Master Reception procedure (DMA)
    ----------------------------------------------------------------*/
	else /* if (numByte >= 2) */	
	{
		/* Test on EV6 and clear it */
		time = I2C_TIMEOUT;
		while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
		{
		    if((time--) == 0)  return FALSE;
		}  
		
		/* Configure the DMA Rx Channel with the buffer address and the buffer size */
		myDmaI2CConfig((uint32_t)&readBuffer, numByte, I2C_Direction_Receiver);

        /* get status of this flag to know when the trasaction is completed */
        I2C_RX_CompleteFlag = FALSE;

		/* Inform the DMA that the next End Of Transfer Signal will be the last one */
		I2C_DMALastTransferCmd(I2Cx, ENABLE); 
		
		/* Enable the DMA Rx Stream */
		DMA_Cmd(DMA_I2C_RX_STREAM, ENABLE); 
        
        while(I2C_RX_CompleteFlag == FALSE);
   
	}
#endif	/* _USE_I2C_DMA_ */

}

/****************************************************************
** Function name:	    myI2CReadBytes
**
** Descriptions:		Write multi-byte (numByte) continuously 
**						begin at start address in I2C slave	by 
**						the NORMAL BUS
**
** parameters:		    None
** Returned value:		None
** 
*****************************************************************/
BOOL myI2CWriteBytes(I2C_TypeDef* I2Cx, uint8_t *writeBuffer, uint8_t devAddr, uint8_t startAddr, uint32_t numByte)
{
	uint32_t time;
    I2C_AcknowledgeConfig(I2Cx, ENABLE); 
	/* While the bus is busy */
	time = I2C_TIMEOUT;
	while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY))
	{
		if((time--) == 0) return FALSE;
	}
	
	/* Send START condition */
	I2C_GenerateSTART(I2Cx, ENABLE);
	
	/* Test on EV5 and clear it (cleared by reading SR1 then writing to DR) */
	time = I2C_TIMEOUT;
	while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
	{
		if((time--) == 0) return FALSE;
	}
	
	/* Send Sensor address for write */
	I2C_Send7bitAddress(I2Cx, devAddr, I2C_Direction_Transmitter);
    	
	/* Test on EV6 and clear it */
	time = I2C_TIMEOUT;
	while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
	{
		if((time--) == 0) return FALSE;
	} 
    	
	/* Send the Register address to read from: Only one byte address */
	I2C_SendData(I2Cx, startAddr);  

    /* Test on EV8 and clear it */
    time = I2C_TIMEOUT;
    while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
        if (time-- == 0) return FALSE;
    }

#ifndef _USE_I2C_DMA_
    /* SHOULD DISABLE INTERRUPT HERE, IF NOT IT MAY BE CORRUPT I2C TRANSACTION */
    __disable_irq();
    while(numByte)
    {
        /* Send the data & increase the pointer of write buffer */
        I2C_SendData(I2Cx, *writeBuffer); 
        writeBuffer++;
        numByte--;  
        /* Test on EV8_2 to ensure data is transmitted, can used EV_8 for faster transmission*/
        time = I2C_TIMEOUT;
        while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
        {
            if ((time--) == 0) return FALSE;
        }  
    }

    /* Send STOP Condition */
    I2C_GenerateSTOP(I2Cx, ENABLE);
    /* ENABLE ALL INTERRUPT */
    __enable_irq();
    return TRUE;

#else /* ifdef _USE_I2C_DMA_ */
    if (numByte < 2)
    {
        /* Send the data */
        I2C_SendData(I2Cx, *writeBuffer);

        /* Test on EV8 and clear it */
        time = I2C_TIMEOUT;
        while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
        {
            if ((time--) == 0) return FALSE;
        }            

        /* Send STOP Condition */
		I2C_GenerateSTOP(I2Cx, ENABLE);	 
    }
    else /* numByte >=2 */
    {
        /* Configure the DMA Tx Channel with the buffer address and the buffer size */
        myDmaI2CConfig((uint32_t)&writeBuffer, numByte, I2C_Direction_Transmitter);
           
        /* Enable the DMA Tx Channel */
        DMA_Cmd(DMA_I2C_TX_STREAM, ENABLE);    
    }
    /* If all operations OK, return TRUE */
    return TRUE;

#endif
  
	
}

/****************************************************************
** Function name:	    DMA_I2C_TX_IRQHandler
**
** Descriptions:		
**
** parameters:		    None
** Returned value:		None
** 
*****************************************************************/
void DMA_I2C_TX_IRQHandler(void)
{
    uint32_t time;
    /* Check if the DMA transfer is complete */ 
    if(DMA_GetFlagStatus(DMA_I2C_TX_STREAM, DMA_I2C_TX_TC ) != RESET)
    {  
        /* Disable the DMA Tx Channel and Clear all its Flags */  
        DMA_Cmd(DMA_I2C_RX_STREAM, DISABLE);
        DMA_ClearFlag(DMA_I2C_RX_STREAM, DMA_I2C_TX_TC);
 
        /* Wait till all data have been physically transferred on the bus */
        time = I2C_TIMEOUT;
        while(!I2C_GetFlagStatus(I2C_SOURCE, I2C_FLAG_BTF))
        {
          if((time--) == 0)  { /* do something here */ }
        }
        
        /* Send STOP condition */
        I2C_GenerateSTOP(I2C_SOURCE, ENABLE);
        
        /* Perform a read on SR1 and SR2 register to clear eventualaly pending flags */
        (void)I2C_SOURCE->SR1;
        (void)I2C_SOURCE->SR2;
        
        I2C_TX_CompleteFlag = TRUE;
 
    }
}

 
void DMA_I2C_RX_IRQHandler(void)
{
    /* Check if the DMA transfer is complete */
    if( DMA_GetFlagStatus(DMA_I2C_RX_STREAM, DMA_I2C_RX_TC) != RESET)
    {      
        /* Send STOP Condition */
        I2C_GenerateSTOP(I2C_SOURCE, ENABLE);    
        
        /* Disable the DMA Rx Channel and Clear all its Flags */  
        DMA_Cmd(DMA_I2C_RX_STREAM, DISABLE);
        DMA_ClearFlag(DMA_I2C_RX_STREAM, DMA_I2C_TX_TC);
        
        I2C_RX_CompleteFlag = TRUE;

    }
}

/****************************************************************
** Function name:	    myI2CReadBytes
**
** Descriptions:		Write multi-byte (numByte) continuously 
**						begin at start address in I2C slave	by 
**						the NORMAL BUS
**
** parameters:		    None
** Returned value:		None
** 
*****************************************************************/
uint32_t myI2CTimeOutCallback(void)
{
	return 0;
}


/****************************************************************
 * END OF standard form.c
 ****************************************************************/

